package monitor

import (
	"encoding/json"
	"fcs-api/src/app/model"
	"fcs-api/src/monitor/data"
	"github.com/prometheus/client_golang/prometheus"
	"log"
	"strings"
	"time"
)

var InvokeMap map[string][]*model.Invoke
var InvokeList []*model.Invoke
var InvokeListStr string
var Region string

/*
	注意：现在不支持给默认采集添加labels
*/

//默认指标采集注册器
//var registryDefualt *prometheus.Registry

//注册监控指标，并收集监控指标
func registryAndGatherMetrics(serviceName string, defaultSwitch bool) {
	log.Println("start registryAndGatherMetrics")
	InvokeMap = map[string][]*model.Invoke{}
	var users []string
	//初始化注册器
	registry = prometheus.NewPedanticRegistry()
	//获取服务监控对象对应资源id
	var resourceIdList []string
	resourceIdList, users = data.GetResourceIdList()
	if len(resourceIdList) == 0 {
		log.Println("registryAndGatherMetrics: resourceList len[%d]", len(resourceIdList))
	}
	log.Println("CreateIdForNameMap:", data.CreateIdForNameMap)
	if Region == "" {
		Region = model.Base.Region
	}
	log.Println("registryAndGatherMetrics: region: ", Region)

	for _, user := range users {
		resourceIdList = append(resourceIdList, Region+"-"+user)
	}

	//获取调用数据
	currentT := time.Now()
	currentTime := currentT.Unix()
	var oldTime int64
	mlis, err := model.GetMetricLmaInvokeList()
	if err != nil {
		log.Println("GetMetricLmaInvokeList err ", err)
	}
	if len(mlis) == 0 { //数据库没有数据
		mli := &model.MetricLmaInvoke{
			Id:              "1",
			InvokeTimestamp: currentTime,
		}
		_, err = model.CreateMetricLmaInvoke(mli)
		if err != nil {
			log.Println("CreateMetricLmaInvoke err ", err)
		}
		oldTime = 0
	} else {
		oldTime = mlis[0].InvokeTimestamp
	}

	InvokeList, err = model.GetInvokesForMonitor(oldTime, currentTime)
	if err != nil {
		log.Println("GetInvokesForMonitor err ", err)
	}
	log.Printf("invokeList len: %d, startTime: %d, endTime: %d", len(InvokeList), time.Unix(oldTime, 0).Format(TimeFormat), time.Unix(currentTime, 0).Format(TimeFormat))

	//更新数据库时间戳
	if oldTime != 0 {
		m := mlis[0]
		m.InvokeTimestamp = currentTime
		_, err := model.UpdateMetricLmaInvoke(m)
		if err != nil {
			log.Println("UpdateMetricLmaInvoke err ", err)
		}
	}

	InvokeListByte, _ := json.Marshal(InvokeList)
	InvokeListStr = string(InvokeListByte)
	if len(InvokeList) != 0 {
		for _, resourceId := range resourceIdList {
			if !strings.Contains(InvokeListStr, resourceId) {
				continue
			}
			m := []*model.Invoke{}
			for _, invoke := range InvokeList {
				if invoke.FuncId == resourceId {
					m = append(m, invoke)
				}
			}
			InvokeMap[resourceId] = m
		}
	}

	//获取配置文件
	config := GetConfig()
	//获取配置文件中metric名和说明
	guageMetric := config.GetStringMapString("metric.guage")
	//生成对应资源监控指标，并注册到采集器
	for name, help := range guageMetric {
		//获取配置文件中dimensions标签字段
		dimensions := config.GetStringSlice("metric.guage." + name + ".dimensions")
		if len(dimensions) <= 0 {
			log.Println("!I dimensions length[%d] is invalid", len(dimensions))
			return
		}
		for _, resourceId := range resourceIdList {
			metric := new(Metric)
			metric.Dimensions = dimensions
			metric.Type = "gauge"
			metric.Name = name
			metric.Help = help
			metric.ResourceId = resourceId

			resourceMetric := NewResourceMetric(serviceName, metric)
			err := registry.Register(resourceMetric)
			if err != nil {
				log.Println("!I the metric collector Register error: %s", err.Error())
			}
		}
	}

	//对接云监控指标
	fcsGuage := config.GetStringMap("metric.fcsGuage")

	//生成对应资源监控指标，并注册到采集器
	for name := range fcsGuage {
		//获取配置文件中dimensions标签字段
		dimensions := config.GetStringSlice("metric.fcsGuage." + name + ".dimensions")
		if len(dimensions) <= 0 {
			log.Println("!I dimensions length[%d] is invalid", len(dimensions))
			return
		}

		if strings.Contains(name, "region") {
			for _, resourceId := range resourceIdList {
				if strings.Contains(resourceId, Region) {
					metric := new(Metric)
					metric.Dimensions = dimensions
					metric.Type = "gauge"
					metric.Name = name
					metric.Help = config.GetString("metric.fcsGuage." + name + ".describe")
					metric.ResourceId = resourceId

					resourceMetric := NewResourceMetric(serviceName, metric)
					err := registry.Register(resourceMetric)
					if err != nil {
						log.Println("!I the metric collector Register error: %s", err.Error())
					}
				}
			}
		} else {
			for _, resourceId := range resourceIdList {
				if !strings.Contains(resourceId, Region) {
					metric := new(Metric)
					metric.Dimensions = dimensions
					metric.Type = "gauge"
					metric.Name = name
					metric.Help = config.GetString("metric.fcsGuage." + name + ".describe")
					metric.ResourceId = resourceId

					resourceMetric := NewResourceMetric(serviceName, metric)
					err := registry.Register(resourceMetric)
					if err != nil {
						log.Println("!I the metric collector Register error: %s", err.Error())
					}
				}
			}
		}
	}

	counterMetric := config.GetStringMapString("metric.counter")
	for name, help := range counterMetric {
		//获取配置文件中dimensions标签字段
		dimensions := config.GetStringSlice("metric.counter." + name + ".dimensions")
		if len(dimensions) <= 0 {
			log.Println("!I dimensions length[%d] is invalid", len(dimensions))
			return
		}
		//注册之前组织数据，Metric类型metric用于组织数据传参
		metric := new(Metric)
		metric.Dimensions = dimensions
		metric.Type = "counter"
		metric.Name = name
		metric.Help = help
		metric.ResourceId = "fcs"

		resourceMetric := NewResourceMetric(serviceName, metric)
		err := registry.Register(resourceMetric)
		if err != nil {
			log.Println("!I the metric collector Register error: %s", err.Error())
		}
	}

	//对接云监控指标
	fcsCounter := config.GetStringMap("metric.fcsCounter")

	//生成对应资源监控指标，并注册到采集器
	for name := range fcsCounter {
		//获取配置文件中dimensions标签字段
		dimensions := config.GetStringSlice("metric.fcsCounter." + name + ".dimensions")
		if len(dimensions) <= 0 {
			log.Println("!I dimensions length[%d] is invalid", len(dimensions))
			return
		}
		if strings.Contains(name, "region") {
			for _, resourceId := range resourceIdList {
				if strings.Contains(resourceId, Region) {
					metric := new(Metric)
					metric.Dimensions = dimensions
					metric.Type = "counter"
					metric.Name = name
					metric.Help = config.GetString("metric.fcsCounter." + name + ".describe")
					metric.ResourceId = resourceId

					resourceMetric := NewResourceMetric(serviceName, metric)
					err := registry.Register(resourceMetric)
					if err != nil {
						log.Println("!I the metric collector Register error: %s", err.Error())
					}
				}
			}
		} else {
			for _, resourceId := range resourceIdList {
				if !strings.Contains(resourceId, Region) {
					metric := new(Metric)
					metric.Dimensions = dimensions
					metric.Type = "counter"
					metric.Name = name
					metric.Help = config.GetString("metric.fcsCounter." + name + ".describe")
					metric.ResourceId = resourceId

					resourceMetric := NewResourceMetric(serviceName, metric)
					err := registry.Register(resourceMetric)
					if err != nil {
						log.Println("!I the metric collector Register error: %s", err.Error())
					}
				}
			}
		}
	}

	// Register Prometheus's default collector
	// defaultSwitch == true: open
	//prometheus.DefaultGatherer自带的监控指标（不加任何标签），go相关和process（详见默认采集文件）
	//defaultSwitch==true生成默认采集
	if defaultSwitch {
		gatherers = prometheus.Gatherers{
			prometheus.DefaultGatherer,
			registry,
		}
	} else {
		gatherers = prometheus.Gatherers{
			registry,
		}
	}
}

//注册监控指标，并收集监控指标
func registryAndGatherPrometheusMetrics() {
	defaultGatherers = prometheus.Gatherers{
		prometheus.DefaultGatherer,
	}

}

//Name + ResourceId can uniquely determine a value
//指标名和资源id唯一确定一条监控指标
type ResourceMetric struct {
	Name       string           //指标名
	Type       string           //指标类型：guage or counter
	ResourceId string           //资源id
	Dimensions []string         //给该资源监控指标打上的维度标签（除了service和resource_id外， 这两个在注册时被使用，属于不可变项）
	MetricDesc *prometheus.Desc //Prometheus注册指标时使用的描述对象
}

// new ResourceMetric and  definition
//新建ResourceMetric对象
func NewResourceMetric(ServiceName string, metric *Metric) *ResourceMetric {
	if ServiceName == "" {
		return nil
	}

	return &ResourceMetric{
		Name:       prometheus.BuildFQName("", "", metric.Name),
		Type:       metric.Type,
		ResourceId: metric.ResourceId,
		Dimensions: metric.Dimensions,
		MetricDesc: prometheus.NewDesc(
			metric.Name,
			metric.Help,
			metric.Dimensions,
			prometheus.Labels{"service": ServiceName, "resource_id": metric.ResourceId},
		)}
}

//计算(评估)监控指标对应值
func (c *ResourceMetric) assessmentResourceMetricValue() (metrics []MetricValue) {
	var metricValues []MetricValue
	switch c.Name {
	case Service_instance_status:
		clusterMetricValue := new(MetricValue)
		clusterMetricValue.Value = 0.0

		dimensions := data.GetDimensionValues(c.Dimensions, c.Name, c.ResourceId, "0")
		clusterMetricValue.DimensionsValue = dimensions
		metricValues = append(metricValues, *clusterMetricValue)
	case Service_status:
		coreMetricValue := new(MetricValue)
		status := data.GetMetricValue(c.Name, c.ResourceId, c.Type)
		if status != "0" {
			coreMetricValue.Value = 1
		} else {
			coreMetricValue.Value = 0
		}
		dimensions := data.GetDimensionValues(c.Dimensions, c.Name, c.ResourceId, status)
		coreMetricValue.DimensionsValue = dimensions
		metricValues = append(metricValues, *coreMetricValue)
	case Fcs_duration:
		fcsMetricValue := GetFcsMetricValue(c.Name, c.Dimensions, c.ResourceId)
		metricValues = append(metricValues, fcsMetricValue)
	case Fcs_avg_memory:
		fcsMetricValue := GetFcsMetricValue(c.Name, c.Dimensions, c.ResourceId)
		metricValues = append(metricValues, fcsMetricValue)
	case Fcs_max_memory:
		fcsMetricValue := GetFcsMetricValue(c.Name, c.Dimensions, c.ResourceId)
		metricValues = append(metricValues, fcsMetricValue)
	case Fcs_function_usage:
		fcsMetricValue := GetFcsMetricValue(c.Name, c.Dimensions, c.ResourceId)
		metricValues = append(metricValues, fcsMetricValue)
	case Fcs_region_duration:
		fcsMetricValue := GetFcsMetricValue(c.Name, c.Dimensions, c.ResourceId)
		metricValues = append(metricValues, fcsMetricValue)
	case Fcs_region_avg_memory:
		fcsMetricValue := GetFcsMetricValue(c.Name, c.Dimensions, c.ResourceId)
		metricValues = append(metricValues, fcsMetricValue)
	case Fcs_region_max_memory:
		fcsMetricValue := GetFcsMetricValue(c.Name, c.Dimensions, c.ResourceId)
		metricValues = append(metricValues, fcsMetricValue)
	case Fcs_region_function_usage:
		fcsMetricValue := GetFcsMetricValue(c.Name, c.Dimensions, c.ResourceId)
		metricValues = append(metricValues, fcsMetricValue)
	case Fcs_total_invocations:
		fcsMetricValue := GetFcsMetricValue(c.Name, c.Dimensions, c.ResourceId)
		metricValues = append(metricValues, fcsMetricValue)
	case Fcs_billable_invocations:
		fcsMetricValue := GetFcsMetricValue(c.Name, c.Dimensions, c.ResourceId)
		metricValues = append(metricValues, fcsMetricValue)
	case Fcs_error_invocations:
		fcsMetricValue := GetFcsMetricValue(c.Name, c.Dimensions, c.ResourceId)
		metricValues = append(metricValues, fcsMetricValue)
	case Fcs_billable_invocations_percent:
		fcsMetricValue := GetFcsMetricValue(c.Name, c.Dimensions, c.ResourceId)
		metricValues = append(metricValues, fcsMetricValue)
	case Fcs_error_invocations_percent:
		fcsMetricValue := GetFcsMetricValue(c.Name, c.Dimensions, c.ResourceId)
		metricValues = append(metricValues, fcsMetricValue)
	case Fcs_region_total_invocations:
		fcsMetricValue := GetFcsMetricValue(c.Name, c.Dimensions, c.ResourceId)
		metricValues = append(metricValues, fcsMetricValue)
	case Fcs_region_billable_invocations:
		fcsMetricValue := GetFcsMetricValue(c.Name, c.Dimensions, c.ResourceId)
		metricValues = append(metricValues, fcsMetricValue)
	case Fcs_region_error_invocations:
		fcsMetricValue := GetFcsMetricValue(c.Name, c.Dimensions, c.ResourceId)
		metricValues = append(metricValues, fcsMetricValue)
	case Fcs_region_billable_invocations_percent:
		fcsMetricValue := GetFcsMetricValue(c.Name, c.Dimensions, c.ResourceId)
		metricValues = append(metricValues, fcsMetricValue)
	case Fcs_region_error_invocations_percent:
		fcsMetricValue := GetFcsMetricValue(c.Name, c.Dimensions, c.ResourceId)
		metricValues = append(metricValues, fcsMetricValue)
	case Fcs_eip_status:
		coreMetricValue := new(MetricValue)
		status := data.GetEipMetricValue()
		if status != "0" {
			coreMetricValue.Value = 1
		} else {
			coreMetricValue.Value = 0
		}
		dimensions := data.GetDimensionValues(c.Dimensions, c.Name, c.ResourceId, status)
		coreMetricValue.DimensionsValue = dimensions
		metricValues = append(metricValues, *coreMetricValue)
	case Fcs_cluster_status:
		coreMetricValue := new(MetricValue)
		status := data.GetClusterMetricValue()
		if status != "0" {
			coreMetricValue.Value = 1
		} else {
			coreMetricValue.Value = 0
		}
		dimensions := data.GetDimensionValues(c.Dimensions, c.Name, c.ResourceId, status)
		coreMetricValue.DimensionsValue = dimensions
		metricValues = append(metricValues, *coreMetricValue)
	case Fcs_virtual_machine:
		coreMetricValue := new(MetricValue)
		status := data.GetVirtualMachineMetricValue()
		if status != "0" {
			coreMetricValue.Value = 1
		} else {
			coreMetricValue.Value = 0
		}
		dimensions := data.GetDimensionValues(c.Dimensions, c.Name, c.ResourceId, status)
		coreMetricValue.DimensionsValue = dimensions
		metricValues = append(metricValues, *coreMetricValue)
	default:
		log.Println("unknown index")
	}
	return metricValues
}

// Describe simply sends the Desc in the struct to the channel.
//描述监控指标，将指标描述放入channel, 等待采集器消费
func (c *ResourceMetric) Describe(ch chan<- *prometheus.Desc) {
	ch <- c.MetricDesc
}

//collect the metric
//采集监控指标数据
func (c *ResourceMetric) Collect(ch chan<- prometheus.Metric) {
	defer registry.Unregister(c)
	switch c.Type {
	case GAUGE:
		metricResult := c.assessmentResourceMetricValue()
		for i := 0; i < len(metricResult); i++ {
			metricResultDimensions := processAndVerifyDimensionsValue(c.Dimensions, metricResult[i].DimensionsValue)
			ch <- prometheus.MustNewConstMetric(
				c.MetricDesc,
				prometheus.GaugeValue,
				float64(metricResult[i].Value),
				metricResultDimensions...,
			)
		}

	case COUNTER:
		metricResult := c.assessmentResourceMetricValue()
		for i := 0; i < len(metricResult); i++ {
			metricResultDimensions := processAndVerifyDimensionsValue(c.Dimensions, metricResult[i].DimensionsValue)
			ch <- prometheus.MustNewConstMetric(
				c.MetricDesc,
				prometheus.CounterValue,
				float64(metricResult[i].Value),
				metricResultDimensions...,
			)
		}
	}
}
